package com.atguigu.gulimall.seckill.service.impl;

import ch.qos.logback.core.net.LoginAuthenticator;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.MemberRespVo;
import com.atguigu.gulimall.seckill.feign.CouponFeignService;
import com.atguigu.gulimall.seckill.feign.ProductFeignService;
import com.atguigu.gulimall.seckill.interceptor.LoginUserInterceptor;
import com.atguigu.gulimall.seckill.service.SeckillService;
import com.atguigu.gulimall.seckill.to.SecKillSkuRedisTo;
import com.atguigu.gulimall.seckill.vo.SeckillSessionWithSKus;
import com.atguigu.gulimall.seckill.vo.SeckillSkuVo;
import com.atguigu.gulimall.seckill.vo.SkuInfoVo;
import com.mysql.cj.Session;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.awt.event.ItemEvent;
import java.sql.Time;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @description:
 * @author: BlueSky
 * @create: 2021-10-12 10:12
 **/
@Service
public class SeckillServiceImpl implements SeckillService {

	@Autowired
	CouponFeignService couponFeignService;

	@Autowired
	ProductFeignService productFeignService;

	@Autowired
	StringRedisTemplate redisTemplate;

	@Autowired
	RabbitTemplate rabbitTemplate;

	@Autowired
	RedissonClient redissonClient;

	private final String SESSIONS_CACHE_PREFIX = "seckill:sessions";
	private final String SKUKILL_CACHE_PREFIX = "seckill:skus";

	private final String SKU_STOCK_SEMAPHONE = "seckill:stock"; //+商品随机码


	@Override
	public void uploadSeckillSkuLatest3Days() {
		//1、扫描最近三天需要参与秒杀的活动
		R session = couponFeignService.getLates3DaySession();
		if (session.getCode() == 0){
			//上架商品
			List<SeckillSessionWithSKus> data = session.getData(new TypeReference<List<SeckillSessionWithSKus>>() {
			});
			//缓存到redis
			//1、缓存活动信息
			saveSessionInfos(data);
			//2、缓存活动的关联商品信息
			saveSessionSkuInfos(data);
		}
	}

	//返回当前时间可以参与的秒杀商品信息
	@Override
	public List<SecKillSkuRedisTo> getCurrentSeckillSkus() {
		//1、确定当前时间属于那个秒杀场次信息
		long time = new Date().getTime();
		Set<String> keys = redisTemplate.keys(SESSIONS_CACHE_PREFIX + "*");
		for (String key : keys) {
			String replace = key.replace(SESSIONS_CACHE_PREFIX, "");
			String[] s = replace.split("_");
			long start = Long.parseLong(s[0]);
			long end = Long.parseLong(s[1]);
			if (time>=start && time<=end){
				//2、获取这个秒杀场次需要的所有商品信息
				List<String> range = redisTemplate.opsForList().range(key, -100, 100);
				BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);
				List<String> list = hashOps.multiGet(range);
				if (list !=null){
					List<SecKillSkuRedisTo> collect = list.stream().map(item -> {
						SecKillSkuRedisTo redis = JSON.parseObject((String) item, SecKillSkuRedisTo.class);
						// redis.setRandomCode(null);	当前秒杀开始就需要随机码
						return redis;
					}).collect(Collectors.toList());
					return collect;
				}
				break;
			}
		}
		return null;
	}

	//获取秒杀预告信息
	@Override
	public SecKillSkuRedisTo getSkuSecKillInfo(Long skuId) {
		//1、找到所有需要参与秒杀的商品key
		BoundHashOperations<String, String , String> hashOps = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);

		Set<String> keys = hashOps.keys();
		if (keys !=null && keys.size() > 0){
			String regex = "\\d_" + skuId;
			for (String key : keys) {

				if (Pattern.matches(regex,key)){
					String json = hashOps.get(key);
					SecKillSkuRedisTo skuRedisTo = JSON.parseObject(json, SecKillSkuRedisTo.class);

					//随机码
					long current = new Date().getTime();
					if (current>=skuRedisTo.getStartTime() && current<=skuRedisTo.getEndTime()){
					}else {
						skuRedisTo.setRandomCode(null);
					}
				}
			}
		}

		return null;
	}

	@Override
	public String kill(String killId, String key, Integer num) throws InterruptedException {
		MemberRespVo respVo = LoginUserInterceptor.loginUser.get();
		//1、获取当前秒杀商品的详细信息
		BoundHashOperations<String, String , String> hashOps = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);

		String s = hashOps.get(killId);
		if (StringUtils.isEmpty(s)){
			return null;
		}else {
			SecKillSkuRedisTo redisTo = JSON.parseObject(s, SecKillSkuRedisTo.class);
			//验证合法性
			Long startTime = redisTo.getStartTime();
			Long endTime = redisTo.getEndTime();
			long time = new Date().getTime();
			long ttl = endTime - time;
			//1、验证时间的合法性
			if (time >= startTime && time <= endTime){
				//2、校验随机码和商品id
				String randomCode = redisTo.getRandomCode();
				String  skuId =redisTo.getPromotionSessionId()+"_"+redisTo.getSkuId();
				if (randomCode.equals(key) && killId.equals(skuId)){
					//3、验证购物数量是否合理
					if (num<=redisTo.getSeckillLimit()){
						//4、验证是否购买过。幂等性；如果只要秒杀成功，就去占位。userId_SessionId_skuId
						//SETNX 不存在就占位
						String redisKey = respVo.getId()+"_"+skuId;
						//自动过期
						Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(redisKey, num.toString(), ttl, TimeUnit.MILLISECONDS);
						if (aBoolean){
							//占位成功说明从来没有买过
							RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHONE);
							try {
								semaphore.tryAcquire(num,100,TimeUnit.MILLISECONDS);
								//秒杀成功
								//快速下单。发送MQ消息
							}catch (InterruptedException e){
								return null;
							}
						}
					}else {
						return null;
					}
				}else {
					return null;
				}
			}else {
				return null;
			}
		}

		return null;
	}

	private void saveSessionInfos(List<SeckillSessionWithSKus> data) {
		data.stream().forEach(session ->{
			long startTime = session.getStartTime().getTime();
			long endTime = session.getEndTime().getTime();
			String key = SESSIONS_CACHE_PREFIX + startTime+"_"+endTime;

			Boolean hasKey = redisTemplate.hasKey(key);
			if (!hasKey){
				//保存为场次id+skuId
				List<String> collect = session.getRelationSkus().stream().map(
						item -> item.getPromotionSessionId().toString()+"_"+item.getSkuId().toString())
						.collect(Collectors.toList());
				//缓存活动信息
				redisTemplate.opsForList().leftPushAll(key,collect);
			}
		});
	}

	private void saveSessionSkuInfos(List<SeckillSessionWithSKus> data) {

		data.stream().forEach(session ->{
			//准备hash操作
			BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);
			session.getRelationSkus().stream().forEach(seckillSkuVo -> {

				//4、随机码
				String token = UUID.randomUUID().toString().replace("-", "");

				if (!redisTemplate.hasKey(seckillSkuVo.getPromotionSessionId().toString()+"_"+seckillSkuVo.getSkuId().toString())){
					//缓存商品
					SecKillSkuRedisTo redisTo = new SecKillSkuRedisTo();
					//1、sku的基本数据
					R skuInfo = productFeignService.getSkuInfo(seckillSkuVo.getSkuId());
					if (skuInfo.getCode() == 0){
						SkuInfoVo info = skuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {
						});
						redisTo.setSkuInfoVo(info);
					}
					//2、sku的秒杀信息
					BeanUtils.copyProperties(seckillSkuVo,redisTo);

					//3、设置上当前商品的秒杀时间信息
					redisTo.setStartTime(session.getStartTime().getTime());
					redisTo.setEndTime(session.getEndTime().getTime());

					redisTo.setRandomCode(token);
					String s = JSON.toJSONString(redisTo);
					ops.put(seckillSkuVo.getPromotionSessionId().toString()+"_"+seckillSkuVo.getSkuId().toString(),s);

					//引入分布式的信号量 限流
					RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHONE + token);
					//商品可以秒杀的数量作为信号量
					semaphore.trySetPermits(seckillSkuVo.getSeckillCount());
				}
			});
		});

	}
}
